#include "simple_timer.h"

namespace qb {
simple_timer::simple_timer() : m_expired(true), m_try_to_expire(false) {}

simple_timer::simple_timer(const simple_timer& t) {
  m_expired = t.m_expired.load();
  m_try_to_expire = t.m_try_to_expire.load();
}

simple_timer::~simple_timer() { stop(); }

void simple_timer::start(int interval, std::function<void()> task) {
  if (m_expired == false) return;

  m_expired = false;
  m_try_to_expire = false;
  std::thread([this, interval, task]() mutable {
    const int basic_unit = 2000;
    const int remainder = interval % basic_unit;
    const int recycles = interval / basic_unit;
    if (0 != recycles) interval = basic_unit;

    int complement = remainder;
    int recycle_count = 1;
    while (!m_try_to_expire) {
#ifdef WIN32
      ::Sleep(interval);  // avoid sleep_until implementation bug.
#else
      std::this_thread::sleep_for(std::chrono::milliseconds(interval));
#endif
      if (recycles == 0)
        task();
      else {
        if (recycles == recycle_count) {
          if (complement == 0) {
            task();

            recycle_count = 1;
            interval = basic_unit;
            complement = remainder;
          } else {
            interval = complement;
            complement = 0;
          }
        } else
          recycle_count++;
      }
    }
    {
      std::lock_guard<std::mutex> locker(m_mutex);
      m_expired = true;
      m_expired_cond.notify_one();
    }
  }).detach();
}

/*
 * this is a asynchronization function, only notify simple_timer to stop, but
 * not wait simple_timer thread exit.
 */
void simple_timer::notify_stop() { m_try_to_expire = true; }

/*
 * this is a synchronization function, notify simple_timer to stop and wait
 * simple_timer thread finishing callback. so this function can't be called
 * in simple_timer callback function.
 */
void simple_timer::stop() {
  if (m_expired || m_try_to_expire) return;

  m_try_to_expire = true;
  {
    std::unique_lock<std::mutex> locker(m_mutex);
    m_expired_cond.wait(locker, [this] { return m_expired == true; });
    m_try_to_expire = false;
  }
}
}  // namespace qb